// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef LOGGING_H
#define LOGGING_H

#include <fstream>
#include <unistd.h>
#include <sys/stat.h>

#ifdef __cplusplus
#include <cstdint>
#include <chrono>
#else
#include <stdint.h>
#endif

#ifdef __ANDROID__
#include "android/log.h"
#else

#ifdef __cplusplus
#include <cstdio>
#else
#include <stdio.h>
#endif

#include <sys/time.h>
#include <syslog.h>

enum AndroidLogPriority {
    ANDROID_LOG_UNKNOWN = 0,
    ANDROID_LOG_DEFAULT,
    ANDROID_LOG_VERBOSE,
    ANDROID_LOG_DEBUG,
    ANDROID_LOG_INFO,
    ANDROID_LOG_WARN,
    ANDROID_LOG_ERROR,
    ANDROID_LOG_FATAL,
    ANDROID_LOG_SILENT
};

using LogCallbackFunction=void (*)(const int level, const char* tag, const char* log);

#endif

#ifdef DEBUG_PRINTOUT  // defined in CMakeLists.txt
#define VMI_DEBUG_LEVEL ANDROID_LOG_DEBUG
#else
#define VMI_DEBUG_LEVEL ANDROID_LOG_INFO
#endif

#ifdef __cplusplus
extern "C" {
#endif

//log
extern std::ofstream logout;

/**
 * @brief: VmiLogPrint, do not use this function directly,use ERR, INFO, DBG, MEMDUMP instead.
 * @param [in] fileName: code file anem
 * @param [in] lineNumber: code line number
 * @param [in] functionName: code function name
 * @param [in] level: print level
 * @param [in] vmiTag: android log tag
 * @param fmt : as fmt in printf
 * @param ... : variable number of arguments
 */
void VmiLogPrint(const char* fileName, int lineNumber, const char* functionName, int level, const char* vmiTag,
                        const char* fmt, ...);
/**
 * @brief: set log level
 * @param [in] level: log level, default is ANDROID_LOG_INFO
 */
void SetLogLevel(int level);

/**
 * @brief: set log file name
 * @param [in] fileName: log file name
 */
void SetLogFileName(const char* logFileName);

/**
 * @brief: print buffer in hexidecimal format, for debug only
 * @param [in] title: name of the buffer
 * @param [in] buffer: buffer pointer
 * @param [in] byteLength: buffer length
 */
void HexDump(const char* title, const uint8_t* buffer, int length);

bool VmiIsLoggable(int level, const char* vmiTag, int defaultLevel);

#ifndef __ANDROID__
/**
 * @brief: set log callback funciton pointer
 *         Note:Multithreaded calls are not supported
 * @param [in] callback: log callback function pointer
 *        if callback is nullptr, then revet to printing with syslog
 */
void SetLogCallback(LogCallbackFunction callback);
#endif

#ifdef __cplusplus
}
#endif

#ifndef LOG_TAG
#define LOG_TAG "Native"
#endif

#define DBG(fmt, ...) VmiLogPrint(__FILE__, __LINE__, __FUNCTION__, ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##__VA_ARGS__)
#define INFO(fmt, ...) VmiLogPrint(__FILE__, __LINE__, __FUNCTION__, ANDROID_LOG_INFO, LOG_TAG, fmt, ##__VA_ARGS__)
#define WARN(fmt, ...) VmiLogPrint(__FILE__, __LINE__, __FUNCTION__, ANDROID_LOG_WARN, LOG_TAG, fmt, ##__VA_ARGS__)
#define ERR(fmt, ...) VmiLogPrint(__FILE__, __LINE__, __FUNCTION__, ANDROID_LOG_ERROR, LOG_TAG, fmt, ##__VA_ARGS__)
#define FATAL(fmt, ...) VmiLogPrint(__FILE__, __LINE__, __FUNCTION__, ANDROID_LOG_FATAL, LOG_TAG, fmt, ##__VA_ARGS__)

#define INFO_TRY(fmt, ...)                                                                                       \
    do {                                                                                                         \
        if(VmiIsLoggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_ERROR)) {                                         \
            VmiLogPrint(__FILE__, __LINE__, __FUNCTION__, ANDROID_LOG_INFO, LOG_TAG, fmt, ##__VA_ARGS__);        \
        }                                                                                                        \
    } while (0)

#define MEMDUMP(buf, len) HexDump("hexdump", buf, len)

/**
 * print only once for second you set
 *  this is not a time-precise macro. it only guarantees that no more than on log could be printed out with in a second.
 */
#define LOG_RATE_LIMIT(level, second, fmt, ...)                                                                  \
    do {                                                                                                         \
        static std::chrono::time_point<std::chrono::steady_clock> lastSecond = std::chrono::steady_clock::now(); \
        auto now = std::chrono::steady_clock::now();                                                             \
        auto diff = now - lastSecond;                                                                            \
        if (std::chrono::duration_cast<std::chrono::seconds>(diff).count() >= second) {                                                                                        \
            VmiLogPrint(__FILE__, __LINE__, __FUNCTION__, level, LOG_TAG, "rate limited: " fmt,                  \
                        ##__VA_ARGS__);                                                                          \
            lastSecond = now;                                                                                    \
        }                                                                                                        \
    } while (0)

#endif /*LOGGING_H*/
